#include "../include/shell.h"
#include "../include/kc.h"
#include "../include/keyboard.h"
#include "../include/string.h"
#include "../include/video.h"
#include "../include/random.h"
#include "../include/list.h"
#include "../include/fs.h"
#include "../include/fifo.h"
#include "../include/game.h"

#define NUMERO_PRIMO 1001

char *prompt = "user@localhost:~";
extern inode** fs;
int path[6][DEEP];
extern terminalActual;
extern int tickpos;
extern void getcpuinfo(void);
extern void getvendorid(void);
extern void getbrand(void);
extern void * memalloc(int size);
extern void reset(void);
extern void _kill(int pid);
extern void _vi(char *s);
extern void _renice(int prioridad, int pid);
extern void _top(void);
extern void _mkdir(void*, void*, void*);
int testlist(int argc, char **argv);
int makedir(int argc, char **argv);
int rrenice(int argc, char **argv);

int listfiles(int argc, char **argv);
int changedir(int argc, char **argv);
int create(int argc, char **argv);
int removedir(int argc, char **argv);
int removefile(int argc, char **argv);
int catt(int argc, char **argv);
int renamme(int argc, char **argv);
int renammedir(int argc, char **argv);
int search(int argc, char **argv);
int tateti(int argc, char **argv);
int printFifo(int argc, char** argv);

comando lista[] = { { "clear", "Limpia la pantalla.", limpiaPantalla },{
		"cpuinfo", "Imprime informacion sobre el procesador", cpuinfo }, {
		"help", "Muestra los comandos disponibles.", imprimeAyuda }, { "top",
		"Muestra el porcentaje de consumo de cada proceso.", top }, {
		"mallocses", "while(1) malloc", mallocInf }, { "kill",
		"Mata a un proceso.", kill }, { "renice",
		"cambia la prioridad a un proceso", rrenice }, { "while1halt",
		"while (1){ halt }", while1halt }, { "newton", "divide por cero",
		divideByZero }, { "vi", "editor de texto", viW }, { "mkdir",
		"crea un ddirectorio", makedir }, { "ls",
		"lista los directorios y archivos", listfiles }, { "cd",
		"cambia directorio", changedir }, { "rmdir", "elimina un directorio",
		removedir }, { "rmfile", "elimina un archivo", removefile }, { "cat",
		"muestra un archivo", catt }, { "rename",
		"cambia el nombre de un dir o un archivo", renamme }, { "renamedir",
		"cambia el nombre de un dir o un archivo", renammedir }, { "find",
		"busca un archivo", search }, {
		"printFifo", "Imprime informacion de los fifos creado", printFifo }, { "tateti",
		"juega al tateti especial con otro jugador en otra terminal", tateti }};

int shell(int a, char **aux) {
	char bufferShell[MAXLONGITUD];
	char argumentos[MAX_ARGC][MAX_ARG];
	char usuario[MAXLONGITUD - 2];
	int background, cmdLen, c, i, offset, comienzo, posActual, argc;

	comienzo = posActual = argc = background = 0;

	initPath(path[terminalActual]);

	imprimePrompt(path[terminalActual]);
	while (1) {

		bufferShell[0] = 0;
		cmdLen = 0;

		while ((c = getChar()) != '\n') {

			if (c != '\b') {
				/* la linea que tipeo cambio, quiero que aparezca como
				 * ultima opcion */
				bufferShell[posActual] = c;
				bufferShell[posActual + 1] = 0;
				posActual++;
				putchar(c);
			} else if (posActual != 0) {
				posActual--;
				bufferShell[posActual] = 0;
				putchar(c);
			}
		}
		offset = 0;

		for (i = 0; i < posActual; i++) {
			if (cmdLen == 0 && bufferShell[i] == ' ') {
				offset++;
				continue;
			}

			if (bufferShell[i] == '\t')
				break;

			/*Parseo de argumentos para kill y renice */
			if (bufferShell[i] == ' ') {
				int len, len2, len3, len4, len5, len6, len7, len8, len9, len10,
						len11;
				len = strlen("renice");
				len2 = strlen("kill");
				len3 = strlen("mkdir");
				len4 = strlen("ls");
				len5 = strlen("creat");
				len6 = strlen("rmdir");
				len7 = strlen("rmfile");
				len8 = strlen("cat");
				len9 = strlen("renamedir");
				len10 = strlen("rename");
				len11 = strlen("find");

				if (strncmp(bufferShell + offset, "kill", len2) == 0
						|| strncmp(bufferShell + offset, "renice", len) == 0
						|| strncmp(bufferShell + offset, "mkdir", len3) == 0
						|| strncmp(bufferShell + offset, "cd", len4) == 0
						|| strncmp(bufferShell + offset, "creat", len5) == 0
						|| strncmp(bufferShell + offset, "rmdir", len6) == 0
						|| strncmp(bufferShell + offset, "cat", len8) == 0
						|| strncmp(bufferShell + offset, "renamedir", len9)
								== 0 || strncmp(bufferShell + offset, "rename",
						len10) == 0 || strncmp(bufferShell + offset, "find",
						len11) == 0) {
					i++;
					int k = 0;
					while (bufferShell[i] == ' ') {
						i++;
					}
					while (/*bufferShell[i] != ' ' &&*/bufferShell[i] != '\n'
							&& i < posActual) {
						if (bufferShell[i] == '\t') {
							i--;
							break;
						}
						if (argc < MAX_ARGC) {
							if (k < MAX_ARG) {
								argumentos[argc][k] = bufferShell[i];
							}
						}
						i++;
						k++;
					}
					if (bufferShell[i + 1] == '\t') {
						break;
					}
					argumentos[argc][k] = '\n';
					argc++;

					if (bufferShell[i] == ' ')
						i--;
				} else
					break;
			}

			/*-----------------------------------------------*/
			cmdLen++;
		}

		if (bufferShell[posActual - 1] == '&') {
			background = 1;
			bufferShell[posActual - 1] = 0;
		} else {
			background = 0;
		}

		/* finalizo la cadena tipeada e imprimo el enter */
		bufferShell[posActual] = 0;
		putchar('\n');

		/* si posActual != 0 es porque hubo algun comando tipeado */
		if (posActual != 0) {
			ejecutaComando(bufferShell + offset, argc, argumentos, background);
		}

		posActual = 0;
		argc = 0;

		/* be nice to mommy and wait :D */
		asm volatile ("hlt");
		asm volatile ("hlt");
		asm volatile ("hlt");
		imprimePrompt(path[terminalActual]);
	}
}

void limpiaLinea(void) {
	int i;
	int fila;
	int len = 2;

	fila = tickpos / (COLS * 2);
	tickpos = fila * COLS * 2 + len * 2;

	for (i = len + 1; i < COLS; i++)
		putchar(' ');

	tickpos = fila * COLS * 2 + len * 2;
	update_cursor(tickpos / 160, (tickpos % 160) / 2);
}

void ejecutaComando(char *cmd, int argc, char argumentos[MAX_ARGC][MAX_ARG],
		int background) {
	char msg[] = "Escriba help para una lista de comandos.\n";
	int cantidad;
	int encontrado;
	int len;
	int i;

	encontrado = 0;
	cantidad = sizeof(lista) / sizeof(lista[0]);

	len = strlen("kill");
	/*Si es un kill */
	if (strncmp(cmd, "kill", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		kill(argc, (char **) argumentos);
		return;
	}

	/*len = strlen("explota");
	 if (strncmp(cmd, "explota", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
	 ejecutar(lista[9].nombre, lista[9].funcion, 1);
	 return;
	 }
	 */
	len = strlen("mkdir");
	if (strncmp(cmd, "mkdir", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		makedir(argc, (char **) argumentos);
		return;
	}

	len = strlen("cd");
	if (strncmp(cmd, "cd", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		changedir(argc, (char **) argumentos);
		return;
	}

	/*len = strlen("creat");
	 if (strncmp(cmd, "creat", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
	 create(argc, (char **) argumentos);
	 return;
	 }*/

	len = strlen("rmdir");
	if (strncmp(cmd, "rmdir", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		removedir(argc, (char **) argumentos);
		return;
	}

	len = strlen("rmfile");
	if (strncmp(cmd, "rmfile", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		removefile(argc, (char **) argumentos);
		return;
	}
	len = strlen("cat");
	if (strncmp(cmd, "cat", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		catt(argc, (char **) argumentos);
		return;
	}
	len = strlen("rename");
	if (strncmp(cmd, "rename", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		renamme(argc, (char **) argumentos);
		return;
	}

	len = strlen("renamedir");
	if (strncmp(cmd, "renamedir", len) == 0 && (cmd[len] == 0 || cmd[len]
			== ' ')) {
		renammedir(argc, (char **) argumentos);
		return;
	}

	len = strlen("renice");
	if (strncmp(cmd, "renice", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		rrenice(argc, (char **) argumentos);
		return;
	}
	len = strlen("find");
	if (strncmp(cmd, "find", len) == 0 && (cmd[len] == 0 || cmd[len] == ' ')) {
		search(argc, (char **) argumentos);
		return;
	}

	for (i = 0; !encontrado && i < cantidad; i++) {
		len = strlen(lista[i].nombre);
		if (strncmp(cmd, lista[i].nombre, len) == 0 && cmd[len] == 0) {
			encontrado = 1;
			ejecutar(lista[i].nombre, lista[i].funcion, background);
		}
	}

	if (!encontrado)
		printf(msg, strlen(msg));
}

int divideByZero(int argc, char **argv) {
	int i;
	i = 2 / 0;
	return 0;
}
int viW(int argc, char **argv) {
	/*llamar al vi;*/
	char *s = "Editor De texto\n";
	char *s1 = "This is a text editor\n";
	char *s2 = "Usage:\n";
	char *s3 = "\t\tq: for quit";
	char **a = NULL;

	int c;
	int i;
	char * text;
	int count = 0;
	limpiaPantalla(0, a);
	//print(name);
	//print("\n");

	for (i = 0; i < 12; i++) {
		print("\n");
	}

	printf(s, 16);
	printf(s1, 22);
	printf(s2, 7);
	printf(s3, 13);
	getChar();
	limpiaPantalla(0, a);

	text = (char *) memalloc(0x100);
	getChar();
	//char text[3000];
	while (1) {
		if ((c = getChar()) == 'q') {
			break;
		}
		text[count] = c;
		putchar(*(text + count));
		count++;
	}
	print("Guardar en:");
	char name[30];
	count = 0;
	while ((c = GetChar()) != '\n') {
		*(name + count) = c;
		putchar(*(name + count));
		count++;
	}
	print("\n");
	/*print(text);
	 GetChar();*/

	/*print("\n***********************\nCopia\n*************************\n");
	 printd(fs);
	 print("\n");
	 fs = mem_copy((void *) text, fs, count);
	 print(
	 "\n***********************\n ya copio los bytes \n*************************\n");
	 printd(fs);
	 print("\n");
	 GetChar();
	 print(
	 "\n***********************\nimprime copia\n*************************\n");
	 print((char*) fs);
	 print("\n");*/

	creat(fs, path[terminalActual], name, (char *) text);
	/*create(1,argv);*/
	return 1;

}/*
 int viW(int argc, char **argv) {
 int i;
 char *argvaux = (char *) argv;

 i = 0;
 while (*(argvaux + i) != '\n') {
 i++;
 }

 argvaux[i] = '\0';

 //	print(argvaux);
 //	getChar();
 vi(argvaux);
 }*/
int cpuinfo(int argc, char **argv) {
	char vid[] = "Vendor ID:";
	char ff[] = "Features:";
	char marca[] = "Brand:";

	print(vid);
	putchar('\n');

	getvendorid();
	putchar('\n');

	print(ff);
	putchar('\n');

	getcpuinfo();
	putchar('\n');

	print(marca);
	putchar('\n');

	getbrand();
	putchar('\n');
}

int mallocInf(int argc, char **argv) {
	char *tmp;
	int i = 0;
	char s[4];
	tmp = (char *) memalloc(512);
	while (tmp != 0x0) {

		printf("malloc me diO: 512 bytes\n", 25);
		tmp = (char *) memalloc(512);
		//*tmp = '3';
	}
	printf("No tengo mas heap :(\n", 21);
}

int kill(int argc, char **argv) {
	int i = 0, numero;
	if (argc == 0) {
		print("No hay parametros\n");
		return;
	}
	char *argvaux = (char*) (argv);
	print((char*) (argv));

	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	if ((numero = atoi(argvaux)) != -1) {

		_kill(numero);

	} else
		printf("Mal ingreso de parametros.\n", 27);

}
int rrenice(int argc, char **argv) {
	int n1, n2, i = 0;
	if (argc == 0) {
		print("No hay parametros\n");
		return;
	}
	char *argvaux = (char*) (argv);
	char *argvaux1 = argv[argc];
	//print((char*)(argv));
	while (*(argvaux + i) != ' ') {
		i++;
	}
	argvaux[i] = '\0';
	i++;
	argvaux1 = (char*) (argvaux + i);
	while (*(argvaux + i) != '\n') {
		i++;
	}
	*(argvaux + i) = '\0';

	if ((n1 = atoi(argvaux)) != -1 && (n2 = atoi(argvaux1)) != -1) {
		_renice(n1, n2);
	} else
		print("Controle los parametros\n");

}
int while1halt(int argc, char **argv) {
	int i;

	while (1) {
		asm volatile("hlt");
	}
}

int imprimeAyuda(int argc, char **argv) {
	int i;
	int cantidad;

	cantidad = sizeof(lista) / sizeof(lista[0]);

	for (i = 0; i < cantidad; i++) {
		printf(lista[i].nombre, strlen(lista[i].nombre));
		putchar('\t');
		putchar('\t');
		printf(lista[i].descripcion, strlen(lista[i].descripcion));
		putchar('\n');
	}
}

void imprimePrompt(int * path) {
	print(prompt);
	print_path(fs, path);
	print("$ ");
}

int limpiaPantalla(int argc, char **argv) {
	int i;

	/* empezar desde la primera posicion */
	tickpos = 0;
	for (i = 0; i < FILAS * COLS * 2; i += 2)
		putchar(' ');

	/* el prompt se debe imprimir en la primera linea */
	tickpos = 0;
	update_cursor(0, 0);
}
/*
 int testlist(int argc, char **argv) {
 listADT lista;
 listElementT elemento;

 print("LISTA12skafndiagnifsjni34\n");

 lista = ListCons(NULL, NULL);

 lista = ListCons("a", lista);
 lista = ListCons("b", lista);
 lista = ListCons("c", lista);
 lista = ListCons("d", lista);
 lista = ListCons("e", lista);

 elemento = ListHead(lista);
 printf(elemento, 1);
 lista = ListTail(lista);
 printf("_________\n", 10);
 elemento = ListHead(lista);
 printf(elemento, 1);
 lista = ListTail(lista);
 printf("_________\n", 10);
 elemento = ListHead(lista);
 printf(elemento, 1);
 lista = ListTail(lista);
 printf("_________\n", 10);
 elemento = ListHead(lista);
 printf(elemento, 1);
 lista = ListTail(lista);
 printf("_________\n", 10);
 elemento = ListHead(lista);
 printf(elemento, 1);
 lista = ListTail(lista);
 printf("_________\n", 10);

 }*/
int makedir(int argc, char **argv) {
	char *argvaux = (char *) argv;
	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';
	_mkdir(fs, path[terminalActual], argvaux);
}
int listfiles(int argc, char **argv) {
	ls(fs, path[terminalActual]);
}
int changedir(int argc, char **argv) {
	char *argvaux = (char *) argv;
	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	_cd(fs, path[terminalActual], argvaux);
}

/*int create(int argc, char **argv) {
 char *argvaux = (char *) argv;
 char * file = "hpsdfnksgmlskdnk kmsglms ksg ksgs ldk dsfnams:";
 int i = 0;
 while (*(argvaux + i) != '\n') {
 i++;
 }
 argvaux[i] = '\0';
 //print("file system\n");
 //printd(fs);
 //print("frint ult elem");

 printd((*(fs + MAX_INODE - 1) + DEEP - 1));
 creat(fs, path[terminalActual], argvaux, file);
 return 0;
 }*/

int removedir(int argc, char **argv) {
	char *argvaux = (char *) argv;

	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	rmdir(fs, path[terminalActual], argvaux);
}

int removefile(int argc, char **argv) {
	char *argvaux = (char *) argv;

	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	rm(fs, path[terminalActual], argvaux);
}
int catt(int argc, char **argv) {
	char *argvaux = (char *) argv;
	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	cat(fs, path[terminalActual], argvaux);
}
int renamme(int argc, char **argv) {
	int i = 0;
	char *argvaux = (char*) (argv);
	char *argvaux1 = argv[argc];
	//print((char*)(argv));
	while (*(argvaux + i) != ' ') {
		i++;
	}
	argvaux[i] = '\0';
	i++;
	argvaux1 = (char*) (argvaux + i);
	while (*(argvaux + i) != '\n') {
		i++;
	}
	*(argvaux + i) = '\0';

	//	print(argvaux);
	/*	print("...");
	 print(argvaux1);
	 print("...");*/
	rename(fs, path[terminalActual], argvaux, argvaux1);
}
int search(int argc, char **argv) {
	char *argvaux = (char *) argv;
	int i = 0;
	while (*(argvaux + i) != '\n') {
		i++;
	}
	argvaux[i] = '\0';

	find(fs, path[terminalActual], argvaux, 1);

}
int renammedir(int argc, char **argv) {

	int i = 0;
	char *argvaux = (char*) (argv);
	char *argvaux1 = argv[argc];
	//print((char*)(argv));
	while (*(argvaux + i) != ' ') {
		i++;
	}
	argvaux[i] = '\0';
	i++;
	argvaux1 = (char*) (argvaux + i);
	while (*(argvaux + i) != '\n') {
		i++;
	}
	*(argvaux + i) = '\0';

	renamedir(fs, path[terminalActual], argvaux, argvaux1);

}


int printFifo(int argc, char ** argv) {
	printFifoTable(1);
}

static int versus[6]; // To know between which terminals are the games played.
int initVersus(void) {

	int i;
	for (i = 0; i < 6; ++i) {
		versus[i] = -1;
	}
	return 0;
}

int tateti(int argc, char **argv) {
	int i;
	int pos;
	char name1[7] = { "truco" }, name2[7] = { "truco" },
			name3[7] = { "truco" }, name4[7] = { "truco" };
	char algo1[2], algo2[2], algo3[2], algo4[2];

	int r_fifo, w_fifo;

	for (i = 0; i < 6; i += 2) {
		if (versus[i + 1] == -1) {
			if (versus[i] != -1) {
				versus[i + 1] = terminalActual;

				itoa(versus[i], algo1);
				itoa(versus[i + 1], algo2);

				strncat(name1, algo1, 1);
				strncat(name2, algo2, 1);

				r_fifo = makeFifo(name1);
				w_fifo = makeFifo(name2);

				/*
				print("los id de los fifos creados son ");
				printd(r_fifo);
				print("_y_");
				printd(w_fifo);
				print("son esos\n");
				*/

				/*
				 print("llamo por que hay 2\n");
				 print("Client escribe en ");
				 printd(versus[i]);
				 print("y lee en");
				 printd(versus[i+1]);
				 print("\n");
				 */

				client(versus[i + 1],versus[i], r_fifo, w_fifo);
				close(r_fifo);
				close(w_fifo);
				return 0;
			} else {
				/*
				print("terminal actual guardando en pos i = ");
				printd(terminalActual);
				print("\n");
				*/
				versus[i] = terminalActual;

				//printd(versus[i]);
				//char numb[10];
				//itoa(terminalActual,numb);
				//print(numb);
				pos = i + 1;
				break;
			}
		}
	}
	print("Waiting for partner\n");
	for (i = 0; i < 30000; ++i) {
		asm volatile ("hlt");
		char name1[2], name2[2];
		if (versus[pos] != -1) {
			/*
			print(
					"llamo por que hice el chequeo y aparecio alguien para jugar\n");
			print("por que se van las lineas al .....\n");

			print("pos = ");
			printd(pos);
			print("\n");
			print("server escribe en ");
			printd(versus[pos-1]);
			print("y lee en");
			printd(versus[pos]);
			print("\n");
			*/
			itoa(versus[pos - 1], algo3);
			itoa(versus[pos], algo4);

			/*
			print("QUR HAY EN VERSUS before server");
			printd(versus[pos - 1]);
			putchar(',');
			printd(versus[pos]);
			print("\n");

			print("Que hay en algo3 y algo4");
			print(algo3);
			putchar(',');
			print(algo4);
			print("\n");
			*/

			strncat(name3, algo3, 1);
			strncat(name4, algo4, 1);

			/*
			print("TIENEN EL MISMO NOMBRE?");
			print("nombre de fifos ");
			print(name3);
			print(" y ");
			print(name4);
			print("\n");

			print("LOS FIFOS ME DEVUELVEN EL MISMO ID AHAHA ");
			printd(openFifo(name4));
			printd(openFifo(name3));
			print("\n");
			*/
			server(versus[pos - 1],versus[pos], openFifo(name4), openFifo(name3));
			return 0;
		}

	}

	// Aca deberia la terminal pasar a estado bloqueado hasta que alguien este listo para jugar y llamar al scheduler
	// Cuando otra terminal este lista para jugar debe despertarla
	return 0;
}
